package com.bacon.gmall.product.service.impl;

import com.bacon.gmall.common.cache.GmallCache;
import com.bacon.gmall.common.constant.RedisConst;
import com.bacon.gmall.model.product.*;
import com.bacon.gmall.product.mapper.*;
import com.bacon.gmall.product.service.ProductService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author： bacon、
 * @version： 1.0
 * @create： 2020-12-22 15:47
 * @description:
 */
@Service
public class ProductServiceImpl implements ProductService {


    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuImageMapper skuImageMapper;

    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;

    @Autowired
    private SpuSaleAttrValueMapper skuSaleAttrValueMapper;

    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient RedissonClient;

    @Autowired
    private BaseTrademarkMapper baseTrademarkMapper;

    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;

    //获取三级分类信息
    @Override
    public BaseCategoryView getBaseCategoryView(Long category3Id) {

        String cacheKey = RedisConst.CATEGORY3ID_PREFIX + category3Id + RedisConst.CATEGORY3ID_SUFFIX;
        BaseCategoryView baseCategoryView = (BaseCategoryView) redisTemplate.opsForValue().get(cacheKey);

        if (null == baseCategoryView) {
            baseCategoryView = baseCategoryViewMapper.selectById(category3Id);

            if (null == baseCategoryView) {
                redisTemplate.opsForValue().set(
                        cacheKey,
                        baseCategoryView,
                        5,
                        TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(
                        cacheKey,
                        baseCategoryView,
                        RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(500),
                        TimeUnit.SECONDS);
            }
        }

        return baseCategoryView;
    }

    @Override
    @GmallCache(prefix = "getSkuInfo")
    public SkuInfo getSkuInfo(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        //判断是否NULL
        if (null != skuInfo) {
            List<SkuImage> skuImageList = skuImageMapper.
                    selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }


    public SkuInfo getSkuInfo1(Long skuId) {

        //尝试获取redis数据
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        String Lock = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;

        //判断redis是否数据
        if (skuInfo == null) {

            /*   * 创建锁
             * 参数一： 多少秒内取拿锁
             * 参数二： 多少秒过期
             * 参数三： 时间类型*/
            try {
                RLock lock = RedissonClient.getLock(Lock);
                boolean tryLock = lock.tryLock(2, 3, TimeUnit.SECONDS);

                if (tryLock) {
                    try {
                        skuInfo = skuInfoMapper.selectById(skuId);

                        //判断数据库中是否有数据
                        if (null == skuInfo) {

                            //防止恶意攻击
                            redisTemplate.opsForValue().set(
                                    cacheKey,
                                    skuInfo,
                                    5 * 60,
                                    TimeUnit.SECONDS);
                            return null;
                        }

                        //返回数据
                        List<SkuImage> skuImageList = skuImageMapper.selectList(
                                new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                        skuInfo.setSkuImageList(skuImageList);

                        //将获取到的数据放入reids中
                        redisTemplate.opsForValue().set(
                                cacheKey,
                                skuInfo,
                                RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(300),
                                TimeUnit.SECONDS);
                    } finally {
                        //解锁
                        lock.unlock();
                    }
                } else {
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //直接从缓存中取
                    skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return skuInfo;
    }


/*
    //获取skuinfo信息
    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        */

    /**
     * 1、查询redis
     * 2、有数据直接返回
     * 3、没有查询sql
     *//*


        //尝试获取redis数据
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        String Lock = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;

        //判断redis是否数据
        if (skuInfo == null) {

            String uuid = UUID.randomUUID().toString();
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, 3, TimeUnit.SECONDS);


            //看是否有人占用锁
            //没有使用返回true
            if (aBoolean) {
                try {
                    skuInfo = skuInfoMapper.selectById(skuId);

                    //判断数据库中是否有数据
                    if (null == skuInfo) {

                        //防止恶意攻击
                        redisTemplate.opsForValue().set(
                                cacheKey,
                                skuInfo,
                                5 * 60,
                                TimeUnit.SECONDS);
                        return null;
                    }

                    //返回数据
                    List<SkuImage> skuImageList = skuImageMapper.selectList(
                            new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                    skuInfo.setSkuImageList(skuImageList);

                    //将获取到的数据放入reids中
                    redisTemplate.opsForValue().set(
                            cacheKey,
                            skuInfo,
                            RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(300),
                            TimeUnit.SECONDS);
                    redisTemplate.delete(Lock);
                } finally {

                    //使用LUA脚本删除锁
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                    this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(Lock), uuid);
                }
            } else {

                //如果没有获取到锁，睡一会
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //直接从缓存中取
                skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
            }

        }
        return skuInfo;
    }
*/

    //获取spu组合信息gy
    @Override
    public Map getSaleAttrValuesBySpu(Long spuId) {

        String cacheKey = RedisConst.SKUKEY_PREFIX + spuId + RedisConst.CATEGORY3ID_SUFFIX;
        Map result = new HashMap<>();

        List<Map> listMap = (List<Map>) redisTemplate.opsForValue().get(cacheKey);
        if (null == listMap) {

            listMap = skuSaleAttrValueMapper.getSaleAttrValuesBySpu(spuId);

            if (null == listMap) {
                redisTemplate.opsForValue().set(cacheKey, listMap, 5, TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(cacheKey, listMap, RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(500), TimeUnit.SECONDS);
            }

        }

        //将上面ListMap转成Map  性能？  不会   Java  -差-> .class --> runtime
        listMap.forEach(map -> {
            result.put(map.get("value_id"), map.get("sku_id"));
        });
        return result;

    }

    @Override
    public List<SpuSaleAttr> getSpuSaleAttrChecked(Long spuId, Long skuId) {

        String cacheKey = RedisConst.SKUKEY_PREFIX + spuId + skuId + RedisConst.CATEGORY3ID_SUFFIX;
        List<SpuSaleAttr> saleAttrList = (List<SpuSaleAttr>) redisTemplate.opsForValue().get(cacheKey);

        if (null == saleAttrList) {
            saleAttrList = spuSaleAttrMapper.getSpuSaleAttrChecked(spuId, skuId);

            if (null == saleAttrList) {
                redisTemplate.opsForValue().set(cacheKey, saleAttrList, 5, TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(cacheKey, saleAttrList, RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(500), TimeUnit.SECONDS);
            }
        }
        return saleAttrList;
    }

    //价钱
    @Override
    public BigDecimal getPrice(Long skuId) {

        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (null != skuInfo) {
            return skuInfo.getPrice();
        }
        return null;
    }

    //查询所有分类
    @Override
    public List<BaseCategoryView> getBaseCategoryViewList() {
        return baseCategoryViewMapper.selectList(null);
    }

    @Override
    public BaseTrademark getBaseTrademark(Long id) {
        return baseTrademarkMapper.selectById(id);
    }

    @Override
    public List<SkuAttrValue> getBaseAttrInfoAndBaseAttrValue(Long skuId) {
        return skuAttrValueMapper.getBaseAttrInfoAndBaseAttrValue(skuId);
    }
}
